/**
 * Copyright 2010 Victor Egea Hernando
 *
 * RockScript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 * 
 * RockScript is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with RockScript.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <ctime>
#include <cstdlib>
#include <iostream>
#include <corelib/corelib.h>
#include <corelib/misc/hash_map>

typedef corelib::hash_map<corelib::ustring, corelib::uint32,
		corelib::ustring_hash> hash_map_t;

// unary test for checking corelib::hash_map template functionality
int main(int argc, char *argv[]) {
	hash_map_t hash_map;

	// set random seed
	srand(time(corelib::null));

	// insert elements
	std::cout << "adding elements with keys: uno, due, trois, four, dez"
			<< std::endl;
	hash_map.insert(hash_map_t::pair_t("uno", 1));
	hash_map.insert(hash_map_t::pair_t("due", 2));
	hash_map.insert(hash_map_t::pair_t("trois", 3));
	hash_map.insert(hash_map_t::pair_t("four", 4));
	hash_map.insert(hash_map_t::pair_t("dez", 10));

	// print values
	std::cout << "number of buckets: " << hash_map.buckets() << std::endl;
	std::cout << "number of elements: " << hash_map.size() << std::endl;
	std::cout << "number of collisions: " << hash_map.collisions() << " (";
	std::cout << (((float) hash_map.collisions() / hash_map.size()) * 100)
			<< "%)" << std::endl;

	std::cout << "iterate hash_map forward and print values" << std::endl;
	// iterate hash_map forward and print values
	for (hash_map_t::const_iterator it = hash_map.begin(); it != hash_map.end();
			++it) {
		std::cout << "pair( '" << it->first << "', " << it->second << " )"
				<< std::endl;
	}

	// delete elements
	std::cout << "deleting 'four': " << std::endl;
	if (hash_map.erase(corelib::ustring("four"))) {
		std::cout << "entry found and deleted (OK)" << std::endl;
	} else {
		return -1;
	}

	std::cout << "deleting 'cinq': " << std::endl;
	if (!hash_map.erase(corelib::ustring("cinq"))) {
		std::cout << "the entry was not found (OK)" << std::endl;
	} else {
		return -1;
	}

	std::cout << "result: " << hash_map.erase(corelib::ustring("cinq"))
			<< std::endl;

	std::cout << "iterate hash_map forward and print values" << std::endl;
	// iterate hash_map forward and print values
	for (hash_map_t::const_iterator it = hash_map.begin(); it != hash_map.end();
			++it) {
		std::cout << "pair( '" << it->first << "', " << it->second << " )"
				<< std::endl;
	}

	// clear table
	corelib::uint32 fails = 0;
	hash_map.clear();
	std::cout
			<< "clear table and add 12000 elements (*without* resizing) to hash_map [ WAIT! ]"
			<< std::endl;
	// add elements...
	for (corelib::uint32 i = 0; i < 12000; i++) {
		// must be big to get good result - rand() is a very weak random function
		std::string str = "easter";
		corelib::uint32 length = rand() % 10000;
		for (corelib::uint32 j = 0; j < length; j++) {
			str.push_back((char) ((rand() % 25) + 97 ));
		}
		// add string to hash_map
		if (!hash_map.insert(hash_map_t::pair_t(str, i)).second) {
			fails++;
		}
	}

	// print fails
	std::cout << "key repeated - insertion fails: " << fails << std::endl;
	// print values
	std::cout << "number of buckets: " << hash_map.buckets() << std::endl;
	std::cout << "number of elements: " << hash_map.size() << std::endl;
	std::cout << "number of collisions: " << hash_map.collisions() << " (";
	std::cout << (((float) hash_map.collisions() / hash_map.size()) * 100)
			<< "%)" << std::endl;

	// clear table
	fails = 0;
	hash_map.clear();
	std::cout
			<< "clear table and add 12000 elements (*with* resizing) to hash_map [ WAIT! ]"
			<< std::endl;
	// add elements...
	for (corelib::uint32 i = 0; i < 12000; i++) {
		// must be big to get good result - rand() is a very weak random function
		std::string str = "easter";
		corelib::uint32 length = rand() % 10000;
		for (corelib::uint32 j = 0; j < length; j++) {
			str.push_back((char) ((rand() % 25) + 97 ));
		}
		// check if a resizing is necessary
		if (((hash_map.size() * 100) / hash_map.buckets()) >= 75) {
			std::cout << "resizing to " << hash_map.buckets() * 2 << " buckets"
					<< std::endl;
			hash_map.resize(hash_map.buckets() * 2);
		}
		// add string to hash_map
		if (!hash_map.insert(hash_map_t::pair_t(str, i)).second) {
			fails++;
		}
	}

	// insert 'easter egg'
	hash_map.insert(hash_map_t::pair_t("easteregg", 666));

	// print fails
	std::cout << "key repeated - insertion fails: " << fails << std::endl;
	// print values
	std::cout << "number of buckets: " << hash_map.buckets() << std::endl;
	std::cout << "number of elements: " << hash_map.size() << std::endl;
	std::cout << "number of collisions: " << hash_map.collisions() << " (";
	std::cout << (((float) hash_map.collisions() / hash_map.size()) * 100)
			<< "%)" << std::endl;

	// change values
	std::cout << "changing all values to 7..." << std::endl;
	for (hash_map_t::iterator it = hash_map.begin(); it != hash_map.end();++it) {
		it->second = 7;
	}

	// find 'trois'
	std::cout << "looking for 'easteregg'..." << std::endl;
	hash_map_t::const_iterator res = hash_map.find("easteregg");

	// check iterator
	if (res != hash_map.end()) {
		std::cout << "found (" << res->second << ")!" << std::endl;
	} else {
		return -1;
	}

	// clear table again
	std::cout << "clear map again" << std::endl;
	hash_map.clear();

}

